package org.cable.account.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import jdk.nashorn.internal.runtime.regexp.JoniRegExp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.cable.account.controller.request.TrafficRequest;
import org.cable.account.controller.request.UseTrafficRequest;
import org.cable.account.feign.ProductFeignService;
import org.cable.account.model.TrafficDO;
import org.cable.account.mapper.TrafficMapper;
import org.cable.account.service.TrafficService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.cable.account.vo.ProductVO;
import org.cable.account.vo.TrafficVO;
import org.cable.account.vo.UserTrafficVo;
import org.cable.common.constants.RedisKey;
import org.cable.common.enums.BizCodeEnum;
import org.cable.common.enums.EventMessageType;
import org.cable.common.exception.BizException;
import org.cable.common.interceptor.LoginInterceptor;
import org.cable.common.model.EventMessage;
import org.cable.common.model.LoginUser;
import org.cable.common.util.JsonData;
import org.cable.common.util.JsonUtil;
import org.cable.common.util.TimeUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hsj
 * @since 2023-10-25
 */
@Service
@Slf4j
public class TrafficServiceImpl extends ServiceImpl<TrafficMapper, TrafficDO> implements TrafficService {

	@Autowired
	private TrafficMapper trafficMapper;

	@Autowired
	private ProductFeignService productFeignService;

	@Autowired
	private RedisTemplate<Object, Object> redisTemplate;

	@Override
	public int add(TrafficDO trafficDO) {
		return trafficMapper.insert(trafficDO);
	}

	@Override
	public IPage<TrafficDO> pageAvailable(int page, int size, long accountNo) {
		Page<TrafficDO> trafficDOPage = new Page<>(page, size);
		String today = TimeUtil.format(new Date(), "yyyy-MM-dd");
		Page<TrafficDO> dbPage = trafficMapper.selectPage(trafficDOPage, new LambdaQueryWrapper<TrafficDO>()
			.eq(TrafficDO::getAccountNo, accountNo)
			.ge(TrafficDO::getExpiredDate, today).orderByDesc(TrafficDO::getGmtCreate));

		return dbPage;
	}

	@Override
	public TrafficDO findByIdAndAccountNo(Long trafficId, Long accountNo) {

		return trafficMapper.selectOne(new LambdaQueryWrapper<TrafficDO>()
			                               .eq(TrafficDO::getAccountNo, accountNo)
			                               .eq(TrafficDO::getId, trafficId));
	}

	@Override
	public int addDaysUsedTimes(long currentTrafficId, Long accountNo, int dayUsedTimes) {

		return trafficMapper.update(null, new LambdaUpdateWrapper<TrafficDO>()
			.eq(TrafficDO::getAccountNo, accountNo)
			.eq(TrafficDO::getId, currentTrafficId)
			.set(TrafficDO::getDayUsed, dayUsedTimes));
	}

	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void handlerTrafficMessage(EventMessage eventMessage) {
		String messageType = eventMessage.getEventMessageType();
		Long accountNo = eventMessage.getAccountNo();
		if (EventMessageType.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
			//订单已经支付,新增流量
			String content = eventMessage.getContent();
			Map<String, Object> map = JsonUtil.json2Obj(content, Map.class);

			//还原订单商品信息
			String outTradeNo = (String) map.get("outTradeNo");
			Integer buyNum = (Integer) map.get("buyNum");
			String productStr = (String) map.get("product");
			ProductVO productVO = JsonUtil.json2Obj(productStr, ProductVO.class);
			log.info("商品信息:{}", productVO);

			//流量包有效期
			LocalDateTime expiredDateTime = LocalDateTime.now().plusDays(productVO.getValidDay());
			Date expiredDate = Date.from(expiredDateTime.atZone(ZoneId.systemDefault()).toInstant());

			TrafficDO trafficDO = TrafficDO.builder().accountNo(accountNo)
			                               .dayLimit(productVO.getDayTimes() * buyNum)
			                               .dayUsed(0).totalLimit(productVO.getTotalTimes())
			                               .pluginType(productVO.getPluginType())
			                               .level(productVO.getLevel())
			                               .outTradeNo(outTradeNo)
			                               .expiredDate(expiredDate).build();

			this.add(trafficDO);
			log.info("消费消息新增流量包:{}", trafficDO);
			/**新增流量包 应该删除这个key*/
			String key = String.format(RedisKey.DAY_TOTAL_TRAFFIC, accountNo);
			redisTemplate.delete(key);

		} else if (EventMessageType.TRAFFIC_FREE_INIT.name().equalsIgnoreCase(messageType)) {
			//发放免费流量包
			Long productId = Long.valueOf(eventMessage.getBizId());

			JsonData result = productFeignService.detail(productId);

			ProductVO productVO = result.getData(new TypeReference<ProductVO>() {});

			//构建流量包对象
			TrafficDO trafficDO = TrafficDO.builder().accountNo(accountNo)
			                               .dayLimit(productVO.getDayTimes())
			                               .dayUsed(0).totalLimit(productVO.getTotalTimes())
			                               .pluginType(productVO.getPluginType())
			                               .level(productVO.getLevel())
			                               .outTradeNo("free_init")
			                               .expiredDate(new Date()).build();

			save(trafficDO);
		}
	}

	@Override
	public Map<String, Object> pageTraffic(TrafficRequest request) {

		int size = request.getSize();
		int page = request.getPage();
		LoginUser loginUser = LoginInterceptor.threadLocal.get();
		IPage<TrafficDO> trafficDOIPage = pageAvailable(page, size, loginUser.getAccountNo());

		//获取流量包列表
		List<TrafficDO> records = trafficDOIPage.getRecords();

		List<TrafficVO> trafficVOList = records.stream().map(obj -> beanProcess(obj)).collect(Collectors.toList());

		HashMap<String, Object> map = new HashMap<>(3);
		map.put("total_records", trafficDOIPage.getTotal());
		map.put("total_page", trafficDOIPage.getPages());
		map.put("current_data", trafficVOList);
		return map;
	}

	private TrafficVO beanProcess(TrafficDO obj) {
		TrafficVO trafficVO = new TrafficVO();
		BeanUtils.copyProperties(obj, trafficVO);
		return trafficVO;
	}

	@Override
	public TrafficVO detail(long trafficId) {
		LoginUser loginUser = LoginInterceptor.threadLocal.get();

		TrafficDO trafficDO = this.findByIdAndAccountNo(trafficId, loginUser.getAccountNo());

		return beanProcess(trafficDO);
	}

	/**
	 * 查询⽤户全部可⽤流量包 遍历⽤户可⽤流量包 判断是否更新-⽤⽇期判断 没更新的流ᰁ包后加⼊【待更新集合】中 增加【今天剩余可⽤总次数】 已经更新的判断是否超过当天使⽤次数 如果没超过则增加【今天剩余可⽤总次数】 超过则忽略
	 * 更新⽤户今⽇流量包相关数据 扣减使⽤的某个流量包使⽤次数
	 *
	 * @param useTrafficRequest
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public JsonData reduce(UseTrafficRequest useTrafficRequest) {
		Long accountNo = useTrafficRequest.getAccountNo();

		//处理流量包,筛选出未更新的流量包,当前使用的流量包
		UserTrafficVo userTrafficVo = processTrafficList(accountNo);
		log.info("今天可用总次数：{},当前使用流量包:{}", userTrafficVo.getDayTotalLeftTimes(), userTrafficVo.getCurrentTraffic());
		if (userTrafficVo.getCurrentTraffic() == null) {
			return JsonData.buildResult(BizCodeEnum.TRAFFIC_REDUCE_FAIL);
		}

		log.info("待更新的流量包列表:{}", userTrafficVo.getUnUpdatedTrafficIds());

		if (CollectionUtils.isNotEmpty(userTrafficVo.getUnUpdatedTrafficIds())) {
			//更新今日流量包
			trafficMapper.batchUpdateUsedTimes(accountNo, userTrafficVo.getUnUpdatedTrafficIds());
		}
		int rows = trafficMapper.addDayUsedTimes(accountNo, userTrafficVo.getCurrentTraffic().getId(), 1);
		if (rows != 1) {
			throw new BizException(BizCodeEnum.TRAFFIC_REDUCE_FAIL);
		}
		//在redis设置下总流量包次数,短链服务那边递减即可,如果有新增流量包,则删除这个key
		long leftSeconds = TimeUtil.getRemainSecondsOneDay(new Date());
		String key = String.format(RedisKey.DAY_TOTAL_TRAFFIC, accountNo);
		redisTemplate.opsForValue().set(key, userTrafficVo.getDayTotalLeftTimes() - 1, leftSeconds, TimeUnit.SECONDS);
		return JsonData.buildSuccess();
	}

	private UserTrafficVo processTrafficList(Long accountNo) {
		//全部流量包
		List<TrafficDO> trafficDOS = trafficMapper.selectAvailableTraffics(accountNo);
		if (CollectionUtils.isEmpty(trafficDOS)) {
			throw new BizException(BizCodeEnum.TRAFFIC_EXCEPTION);
		}
		//天剩余可用总次数
		Integer dayTotalLeftTimes = 0;

		//当前使用
		TrafficDO currentTrafficDo = null;

		//没过期,但今天没更新的流量包ID
		List<Long> unUpdatedTrafficIds = new ArrayList<>();

		//今天的日期
		String todayStr = TimeUtil.format(new Date(), "yyyy-MM-dd");

		for (TrafficDO trafficDO : trafficDOS) {
			String trafficUpdateDate = TimeUtil.format(trafficDO.getGmtModified(), "yyyy-MM-dd");
			if (todayStr.equalsIgnoreCase(trafficUpdateDate)) {
				//已经更新 剩余可用的总次数 =总次数-已用
				int dayLeftTimes = trafficDO.getDayLimit() - trafficDO.getDayUsed();

				//选取当次使用流量包
				if (dayLeftTimes > 0 && currentTrafficDo == null) {
					currentTrafficDo = trafficDO;
				}
			} else {
				//未更新
				dayTotalLeftTimes = dayTotalLeftTimes + trafficDO.getDayLimit();
				//记录未更新的流量包
				unUpdatedTrafficIds.add(trafficDO.getId());
				//选取当次使用的流量包
				if (currentTrafficDo == null) {
					currentTrafficDo = trafficDO;
				}
			}
		}

		UserTrafficVo userTrafficVo = new UserTrafficVo(dayTotalLeftTimes, currentTrafficDo, unUpdatedTrafficIds);

		return userTrafficVo;

	}

	@Override
	public boolean deleteExpireTraffic() {
		return trafficMapper.deleteExpireTraffic();
	}

}
